#include <bits/stdc++.h>
using namespace std;

#define MAX_N 1000
#define MAX_M 100000

// int friends[MAX_N]; // 朋友节点表
// int animies[MAX_M];   // 敌人节点表

// int dpF[MAX_N]; // 朋友集合的秩（树的深度）
// int dpA[MAX_N]; // 敌人集合的秩（树的深度）

// // 带秩并查集初始化
// inline void init(int n)
// {
//     for (int i = 1; i <= n; ++i)
//     {
//         // 初始时，每个节点都是独立的
//         friends[i] = i;
//         animies[i] = i;

//         // 每个节点的秩都是1
//         dpF[i] = 1;
//         dpA[i] = 1;
//     }
// }

// // 找到元素x所在的集合set的根
// int find(int set[], int x)
// {
//     // 如果要找的节点刚好就是集合的根
//     // 那么直接返回其自身
//     if(x == set[x])
//         return x;

//     // 路径压缩
//     // 顺着关系往根元素查找的同时
//     // 将路径上所有元素的朋友节点全部压缩为根节点
//     set[x] = find(set, set[x]);
//     return set[x];
// }

// // 元素合并
// inline void merge(int set[], int dp[], int i, int j)
// {
//     // 分别找到元素i和j的根元素x和y
//     int x = find(set, i);
//     int y = find(set, j);

//     // 如果x所在的集合的秩（树的深度）比较小
//     // 那么把x的父元素设置为y
//     if (dp[x] <= dp[y])
//         set[x] = y;
//     // 否则把y的父元素设置为x
//     else
//         set[y] = x;

//     // 如果x和y的秩一样，且i和j分属不同集合
//     // 那么作为新的根元素的秩要+1
//     if (dp[x] == dp[y] && x != y)
//         dp[y]++;
// }

// int main()
// {
//     // n个人
//     // m对敌友关系
//     int n, m;
//     cin >> n >> m;

//     init(n);

//     int p, x, y;
//     for (int i = 0; i < m; ++i)
//     {
//         cin >> p >> x >> y;

//         // 若是朋友关系，则合并到friends中
//         if(p == 0)
//             merge(friends, dpF, x, y);

//         // 若是敌人关系，则合并到animies中
//         else
//             merge(animies, dpA, x, y);
//     }

//     // 合并某个人的所有敌人
//     for(int i=0; i<n; i++)
//     {

//     }

//     return 0;
// }


















int n;
int m;
int h[MAX_N + 5], p[MAX_M + MAX_M + 5], t[MAX_M + MAX_M + 5];

int r[MAX_N + 5]; // 保存敌人的集合
int c[MAX_N + 5];
int ans;

// 在集合r中找到元素x
int find(int x)
{
    int R = x, tmp;
    // 沿着叶子路径找到根节点
    while(R != r[R])
        R = r[R];

    // 将集合中的节点变成扇形
    while(x != r[x])
    {
        tmp = r[x];
        r[x] = R;
        x = tmp;
    }
    return R;
}

void initDisjointSet()
{
    // 初始时，所有人各自为营
    for(int i=1; i<n; ++i)
        r[i] = i;
}

// 并查集的经典合并操作
void merge(int x, int y)
{
    if(find(x) != find(y))
        r[find(y)] = find(x);
}

int main()
{
    cin >> n >> m;

    initDisjointSet();

    int f, x, y;
    for( int i=1; i<=m; ++i)
    {
        cin >> f >> x >> y;

        // x和y是敌人
        if(f == 1)
        {
            // 将所有的敌人做一个记录
            t[i] = y;
            p[i] = h[x];
            h[x] = i;

            t[i + m] = x;
            p[i + m] = h[y];
            h[y] = i + m;
        }

        // x和y是朋友，则合并他们
        else
            merge(x, y);
    }

    for( int i=1; i<=n; ++i)
    {
        x = t[h[i]];

        for( int j=h[i]; j!=0; j=p[j])
            merge(x, t[j]);
    }

    for( int i = 1; i <= n; ++i)
        c[find(i)] = 1;

    for( int i = 1; i <= n; ++i)
        ans += c[i];

    cout << ans << endl;
    return 0;
}